import base64
from datetime import datetime, timedelta
from decimal import Decimal

from django.utils import timezone
import io

import stripe
from django.core.mail import EmailMultiAlternatives
from django.db import transaction
from django.db.models import Subquery, OuterRef, Sum, Q
from django.forms import model_to_dict
from mimetypes import guess_type

from django.shortcuts import get_object_or_404
from django.template.loader import get_template
from rest_framework.decorators import api_view, permission_classes, authentication_classes
from rest_framework.permissions import IsAdminUser
from rest_framework.response import Response
from rest_framework_simplejwt.authentication import JWTAuthentication
from six import BytesIO
import io


from apps.billing.filters import InvoiceFilter, invoice_filter, client_invoice_filter_queryset
from apps.billing.helpers.create_charge_by_stripe import create_charge_by_stripe
from apps.billing.models import Invoice, ClientInvoice, ClientPayment, InvoiceServices
from rest_framework import status

from django.conf import settings

from apps.booking.models import Booking, Appointment, Attendance
from apps.core.pagination import paginated_queryset_response
from apps.document.models import AppointmentNotes
from apps.location.models import Room
from apps.payment.utils import billing_helpers

from apps.core.utils import date_time as datetime_helper
from apps.tenant.models import InvoiceSetting, ProviderServices
from apps.users.permissions import IsProvider, IsClient
from django.template import Template, Context
from .types import *
from apps.client.models import Client, ClientAddress
from .models import SuperBill, increment_super_bill_number
from django.core.exceptions import ObjectDoesNotExist
from rest_framework.validators import ValidationError

from ..booking.helpers import appointment_services
from ..document.types import NoteType

SMALLEST_CURRENCY_UNIT = Decimal(settings.SMALLEST_CURRENCY_UNIT)


# Create your views here.

@api_view(('GET', 'POST'))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def invoice_api(request):
    if request.method == 'POST':
        data = request.data
        data['due_date'] = datetime.fromtimestamp(data['due_date'])
        booking = data.pop('bookings')
        invoice = Invoice.objects.create(**data)
        invoice.bookings.set(booking)

        return Response({"msg": "Invoice has been created"}, status=status.HTTP_201_CREATED)

    if request.method == 'GET':
        ordering = request.GET.get('ordering', '-created_at')
        queryset = Invoice.objects.all().order_by(ordering)

        queryset = invoice_filter(queryset=queryset, request=request)
        offset = int(request.GET.get('offset', 0))
        limit = int(request.GET.get('limit', 10))
        count = queryset.count()
        queryset = queryset[offset: offset+limit]
        data = []
        for query in queryset:
            # booking_data = []
            # bookings = query.bookings.all()
            # for booking in bookings:
            #     booking_data.append({'id': booking.id, 'start_time': booking.start_time, 'end_time': booking.end_time})
            # query = model_to_dict(query)
            # query['bookings'] = booking_data
            invoice = {
                'id': query.id,
                'due_date': query.due_date.timestamp() if query.due_date else None,
                'invoice_number': query.invoice_number,
                'total_amount': query.total_amount if query.total_amount else 0,
                'status': query.status

            }
            data.append(invoice)

        return Response({'count': count, 'offset': offset, 'limit': limit, 'result': data})


@api_view(('GET', 'POST'))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider | IsAdminUser])
@transaction.atomic
def invoice_id_api(request, invoice_id=None):
    invoice = get_object_or_404(Invoice, id=invoice_id)

    if request.method == 'POST':
        provider = request.user
        from_email = settings.CONTACT_EMAIL
        to_email = provider.email

        if 'file' in request.data:
            file_b64 = request.data['file']
            file_b64 = file_b64.split(',')[-1]
            buffer = BytesIO.BytesIO()
            content = base64.b64decode(file_b64)
            buffer.write(content)

            html_template = get_template('provider_invoice_email_template.html')
            context = {
                'full_name_provider': provider.first_name + ' ' + provider.last_name,
            }
            html_content = html_template.render(context)

            msg = EmailMultiAlternatives(
                subject="Ethera - Invoice", from_email=from_email, to=[to_email])
            msg.attach_alternative(html_content, "text/html")
            msg.attach(f"invoice_{invoice.invoice_number}.pdf", buffer.getvalue(),
                       guess_type(f"invoice_{invoice.invoice_number}.pdf")[0])
            try:
                msg.send()
                return Response({"response": "Email Sent"}, status=status.HTTP_200_OK)
            except Exception as ex:
                print(ex)
        else:
            return Response({"error": "Please attach file."}, status=status.HTTP_400_BAD_REQUEST)

    if request.method == 'GET':
        bookings = invoice.bookings.all()
        booking_data = []
        provider = request.user.provider
        for booking in bookings:
            room = Room.objects.filter(id=booking.room).first()
            prices = booking.prices.all()[0] if booking.prices.exists() else None

            data = {'id': booking.id, 'start_time': booking.start_time, 'room_cost': booking.room_cost,
                    'booking_number': booking.booking_number,
                    'cancel_date': booking.cancel_date.timestamp() if booking.cancel_date else None,
                    'created_at': booking.created_at.timestamp(),
                    'start_date': booking.start_date.timestamp(), 'status': booking.status,
                    'end_time': booking.end_time, 'room': {
                    'id': room.id,
                    'name': room.name,
                }, 'location': {'id': room.location.id, 'name': room.location.name}}
            if prices:
                data['prices'] = {'id': prices.id, 'tier': prices.tier, 'hours': prices.hours,
                                 'price': prices.price}
            booking_data.append(data)
        _created_at = invoice.created_at.timestamp() if invoice.created_at else None
        _due_date = invoice.due_date.timestamp() if invoice.due_date else None
        invoice = model_to_dict(invoice)
        invoice['bookings'] = booking_data
        invoice['created_at'] = _created_at if _created_at else None
        invoice['due_date'] = _due_date
        invoice['provider'] = {
                'id': provider.id,
                'first_name': provider.first_name,
                'middle_name': provider.middle_name,
                'last_name': provider.last_name
            }

        return Response(invoice)


@api_view(('GET',))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def invoice_charging_api(request, invoice_id=None):
    invoice = get_object_or_404(Invoice, id=invoice_id)

    if request.method == 'GET':
        check_overdue = 'date' not in request.query_params
        # trigger_date = request.query_params.get('date', False)
        # if trigger_date and not self.is_not_the_future(trigger_date):
        #     return Response(
        #         {'message': 'Please do not select a future date!'},
        #         status=status.HTTP_406_NOT_ACCEPTABLE)
        invoices = billing_helpers.filter_invoices(request)
        for invoice in invoices:
            billing_helpers.charge_invoice(invoice, check_overdue)

        return Response(None, status=status.HTTP_201_CREATED)


@api_view(('GET',))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def reports_api(request):
    if request.method == 'GET':
        year = datetime_helper.get_starting_time_range('year')
        current_month = datetime_helper.get_time_range('month')
        next_month = datetime_helper.range_of_us_next_month()

        basic_invoices = ClientPayment.objects.all()
        year = [year.date() for year in year]
        invoices = basic_invoices.filter(created_at__date__range=year)
        ytd_income = invoices.aggregate(
            Sum('price'))
        ytd_appointment = invoices.count()
        ytd_clients = invoices.filter(invoice__appointment__isnull=False).values(
            'invoice__appointment__clients').distinct().count()
        basic_invoices = ClientInvoice.objects.all()
        invoices = basic_invoices.filter(issue_date__range=current_month).exclude(
            client_payments__in=Subquery(ClientPayment.objects.filter(invoice=OuterRef('pk')).values('invoice')))

        current_income = invoices.aggregate(Sum('fees'))
        current_appointment = invoices.count()
        current_clients = invoices.filter(appointment__isnull=False).values(
            'appointment__clients').distinct().count()

        invoices = basic_invoices.filter(issue_date__range=next_month)

        next_month_income = invoices.aggregate(Sum('fees'))
        next_month_appointment = invoices.count()
        next_month_clients = invoices.filter(appointment__isnull=False).values(
            'appointment__clients').distinct().count()

        return Response({'ytd_income': ytd_income, 'ytd_appointment': ytd_appointment, 'ytd_clients': ytd_clients,
                         'current_income': current_income, 'current_appointment': current_appointment,
                         'current_clients': current_clients,
                         'next_month_income': next_month_income, 'next_month_appointment': next_month_appointment,
                         'next_month_clients': next_month_clients}, 201)


def client_invoice_api(request, invoice_id):
    invoice = get_object_or_404(ClientInvoice, id=invoice_id)
    if request.method == 'POST':
        provider = request.user
        invoice_setting = InvoiceSetting.objects.all().first()
        from_email = invoice_setting.email
        clients = invoice.clients.all()
        if 'file' in request.data:
            file_b64 = request.data['file']
            file_b64 = file_b64.split(',')[-1]
            buffer = BytesIO.BytesIO()
            content = base64.b64decode(file_b64)
            buffer.write(content)
            for client in clients:
                context = {
                    'message': invoice_setting.message,
                    'number_of_invoices': invoice.invoice_number,
                    'client_name': client.first_name,
                    'provider_name': (provider.first_name + ' ' +
                                      provider.last_name),
                    'provider_email': provider.email,
                    'provider_phone_number': provider.phone_number
                }
                message = invoice_setting.message
                if invoice_setting.include_logo:
                    logo_template = f'<img src="{invoice_setting.logo_image}images/ethera_logo.png" alt="Logo"><br/>'
                    message = logo_template + message
                if invoice_setting.footer_information:
                    message = message + invoice_setting.footer_information
                message_template = Template(message)
                html_content = message_template.render(Context(context))

                msg = EmailMultiAlternatives(
                    subject=str(invoice_setting.subject).format(date=invoice.created_at), from_email=from_email,
                    to=[client.email])
                msg.attach_alternative(html_content, "text/html")

                msg.attach(f"invoice_{invoice.invoice_number}.pdf", buffer.getvalue(),
                           guess_type(f"invoice_{invoice.invoice_number}.pdf")[0])

                try:
                    msg.send()
                except Exception as ex:
                    print(ex)
            return Response({"response": "Email Sent"}, status=status.HTTP_200_OK)
        else:
            return Response({"error": "Please attach file."}, status=status.HTTP_400_BAD_REQUEST)


@api_view(('POST',))
@authentication_classes([JWTAuthentication])
@permission_classes([IsClient])
@transaction.atomic
def charge_by_stripe(request, pk):
    try:
        user = request.user
        data = request.data

        client_charge = ClientInvoice.objects.filter(id=pk).first()

        price = client_charge.fees
        charge = create_charge_by_stripe(user, price, data, pk, request)

        return Response(charge.data, status=charge.status_code)

    except stripe.error.StripeError as e:
        return Response({'error': e.error.code},
                        status=status.HTTP_400_BAD_REQUEST)


@api_view(['GET'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def appointment_invoice(request, pk):
    try:
        invoice = ClientInvoice.objects.filter(appointment__id=pk).values('id', 'invoice_number', 'status').first()
        if invoice is None:
            return Response({"detail": "Invoice not found"}, status=status.HTTP_404_NOT_FOUND)
        return Response(invoice, status=status.HTTP_200_OK)
    except Exception as e:
        return Response({"detail": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

@api_view(['GET'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def list_client_payments(request):
    try:
        queryset = ClientPayment.objects.all()
        start_date_str = request.GET.get('start_date')
        end_date_str = request.GET.get('end_date')
        if start_date_str and end_date_str:
            start_date = datetime.strptime(start_date_str, '%Y-%m-%d')
            end_date = datetime.strptime(end_date_str, '%Y-%m-%d')

            queryset = queryset.filter(date_received__range=(start_date, end_date))

        data = []
        for payment in queryset:
            payment_data = {
                'id': payment.id,
                'date_received': payment.date_received,
                'price': payment.price,
                'status': payment.status,
            }
            data.append(payment_data)

        return paginated_queryset_response(data, request)
    except Exception as e:
        return Response({
            'success': False,
            'message': f'Failed tp perform GET operation on list_client_payments because of {str(e)}'
        }, status=status.HTTP_400_BAD_REQUEST)


def filtering_admin_notes(request, notes):
    params = request.query_params
    if 'start_date' in params and 'end_date' not in params:
        start_date = params['start_date']
        notes = notes.filter(date=start_date)

    elif 'start_date' in params and 'end_date' in params:
        start_date = params['start_date']
        end_date = params['end_date']

        if start_date > end_date:
            raise ValidationError(
                'Start date must be lower than end date.')

        notes = notes.filter(date__range=[start_date, end_date])

    return notes


@api_view(['GET'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def list_appointments_with_notes(request):
    try:
        current_datetime = timezone.now()
        queryset = Appointment.objects.filter(
            Q(start_date__lt=current_datetime.date())|
            Q(booking__start_date__lt=current_datetime.date())
        ).exclude(
            start_date=current_datetime.date(),
            start_time__gte=current_datetime.time()
        )

        if 'start_date' in request.query_params and 'end_date' in request.query_params:
            start_date = request.query_params.get('start_date', None)
            end_date = request.query_params.get('end_date', None)
            if start_date and end_date:
                queryset = queryset.filter(Q(start_date__range=(start_date, end_date))|Q(booking__start_date__range=(start_date, end_date)))

        queryset = list(queryset)
        client_id = request.query_params.get('clients')
        notes = None
        if client_id:
            notes = AppointmentNotes.objects.filter(client__id=client_id,type=NoteType.ADMIN_NOTE.value, appointment__isnull=True)
            notes = list(filtering_admin_notes(request, notes))

        # appointments = [{'id': appointment.id,
        #                  # 'date_received': appointment.date_received,
        #                  # 'price': appointment.price,
        #                  'status': appointment.status,
        #
        #                  } for appointment in queryset]
        appointments = []
        for appointment in queryset:
            start_date = appointment.start_date if appointment.start_date else None
            if start_date is None:
                start_date = appointment.booking.start_date if appointment.booking else None

            data = {
                    'id': appointment.id,
                    'status': appointment.status,
                    'start_date': start_date.timestamp() if start_date else None,
                    'start_time': appointment.start_time,
                    'end_time': appointment.end_time,
                    'appointment_services': appointment_services(appointment),
                    'appointment_notes': []
                    }

            for note in appointment.appointment_notes.all():
                data_dict = {
                    'id': note.id,
                    'content': note.content,
                    'type': note.type,
                    'title': note.title,
                    'appointment_notes_files': []
                }
                for file in note.appointment_notes_files.all():
                    file_dict = {
                        'file': file.file.url if file.file else None,
                        'title': file.title if file.title else None,
                        'id': file.id
                    }
                    data_dict['appointment_notes_files'].append(file_dict)
                data['appointment_notes'].append(data_dict)


            client_data = []
            for client in appointment.clients.all():
                client_data.append({
                    'id': client.id,
                    'first_name': client.first_name,
                    'middle_name': client.middle_name,
                    'last_name': client.last_name
                })

            data['clients'] = client_data
            appointments.append(data)

        if notes:
            admin_notes = [{'id': note.id,
                            'appointment': note.appointment,
                            'title': note.title,
                            'content': note.content,
                            'type': note.type,
                            'date': note.date.timestamp(),
                            'client': note.client.id,
                            'time': note.time,
                            'note': note.note,
                            } for note in notes]
        else:
            admin_notes = []

        merged_data = sorted(
            appointments + admin_notes,
            key=lambda x: x.get('start_date') or x.get('date', ''),
            reverse=True
        )
        response_data = {'data': merged_data}

        # return Response({'result': response_data['data']})
        return Response({'result': response_data['data']}, status=status.HTTP_200_OK)
    except Exception as e:
        return Response({
            'success': False,
            'message': f'Failed to perform GET operation on list_appointments_with_notes because of {str(e)}'
        }, status=status.HTTP_400_BAD_REQUEST)


@api_view(('GET', 'POST'))
@permission_classes([IsProvider | IsClient])
def client_invoices(request):
    try:
        if request.method == 'GET':
            user = request.user
            get_status = request.GET.get('status')
            queryset = ClientInvoice.objects.all().order_by('-created_at')
            if user.is_client:
                queryset = queryset.filter(client=user)
                provider_id = ''
            else:
                queryset = client_invoice_filter_queryset(request=request, queryset=queryset)
                provider_id = str(request.user.provider.id)

            if get_status:
                queryset = queryset.filter(status=int(get_status))

            data = []
            for invoice in queryset:
                super_bill = invoice.super_bill.first()
                invoice_data = {
                    'id': invoice.id,
                    'invoice_number': invoice.invoice_number,
                    'appointment_id': invoice.appointment_id,
                    'status': invoice.status,
                    'provider_id': provider_id,
                    'issue_date': str(int(invoice.issue_date.timestamp())) if invoice.issue_date else None,
                    'created_at': str(int(invoice.created_at.timestamp())) if invoice.created_at else None,
                    'first_name': invoice.client.first_name,
                    'last_name': invoice.client.last_name,
                    'billing_type': invoice.billing_type,
                    'fees': str(invoice.fees),
                    'due_date': str(int(invoice.due_date.timestamp())) if invoice.due_date else None,
                }
                if super_bill:
                    invoice_data['super_bill_id'] = super_bill.id
                    invoice_data['super_bill_numbers'] = super_bill.bill_number
                invoice_services_data = []
                for service in InvoiceServices.objects.filter(invoice=invoice):
                    service_data = {
                        'date': str(int(service.date.timestamp())) if service.date else None,
                        'service': {
                            'id': str(service.service.id),
                            'updated_at': str(int(service.service.updated_at.timestamp())),
                            'created_at': str(int(service.service.created_at.timestamp())),
                            'code': service.service.code,
                            'service': service.service.service,
                            'time': service.service.time,
                            'fee': service.service.fee,
                            # 'provider': str(request.user.provider.id),
                        },
                        'fees': str(service.fees),
                    }
                    invoice_services_data.append(service_data)

                invoice_data['invoice_services'] = invoice_services_data
                invoice_data['client'] = {
                    "id": invoice.client.id,
                    "first_name": invoice.client.first_name,
                    "last_name": invoice.client.last_name,
                }
                data.append(invoice_data)

            return paginated_queryset_response(data, request)
        if request.method == 'POST':
            get_client = Client.objects.filter(id=request.data['client']).first()
            if not get_client:
                return Response({
                    'success': False,
                    'message': 'Client does not exist'
                }, status=status.HTTP_404_NOT_FOUND)
            request_data = request.data.copy()
            client_invoice_payload = {
                'issue_date': datetime.fromtimestamp(int(request_data['issue_date'])),
                'due_date': timezone.now() + timedelta(days=7),
                'fees': request_data['fees'],
                'client': get_client,
            }
            invoice = ClientInvoice.objects.create(**client_invoice_payload)
            for service in request_data['services']:
                client_invoice_service_payload = dict()
                get_service = ProviderServices.objects.get(id=service['service'])
                client_invoice_service_payload = {
                    'invoice': invoice,
                    'date': datetime.fromtimestamp(int(service['date'])),
                    'service': get_service,
                    'client': get_client,
                    'fees': service['fees']
                }
                InvoiceServices.objects.create(**client_invoice_service_payload)

            return Response({'id': invoice.id}, status=status.HTTP_201_CREATED)
    except Exception as e:
        return Response({
            'success': False,
            'message': f'Failed to perform {request.method} action on client_invoices because of {str(e)}'
        }, status=status.HTTP_400_BAD_REQUEST)


@api_view(['GET', 'PATCH', 'DELETE'])
@permission_classes([IsProvider | IsClient])
def retrieve_and_update_client_invoice(request, pk):
    try:
        if request.method == 'GET':
            user = request.user
            invoice = ClientInvoice.objects.filter(id=pk).first()
            if user.is_client:
                provider_id = ''
            else:
                provider_id = str(request.user.provider.id)
            invoice_data = {
                'id': invoice.id,
                'invoice_number': invoice.invoice_number,
                'appointment_id': invoice.appointment_id,
                'status': invoice.status,
                'provider_id': provider_id,
                'issue_date': str(int(invoice.issue_date.timestamp())) if invoice.issue_date else None,
                'created_at': str(int(invoice.created_at.timestamp())) if invoice.created_at else None,
                'first_name': invoice.client.first_name,
                'last_name': invoice.client.last_name,
                'billing_type': invoice.billing_type,
                'fees': str(invoice.fees),
                'due_date': str(int(invoice.due_date.timestamp())) if invoice.due_date else None,
            }
            invoice_services_data = []
            for service in InvoiceServices.objects.filter(invoice=invoice):
                service_data = {
                    'date': str(int(service.date.timestamp())) if service.date else None,
                    'service': {
                        'id': str(service.service.id),
                        'updated_at': str(int(service.service.updated_at.timestamp())),
                        'created_at': str(int(service.service.created_at.timestamp())),
                        'code': service.service.code,
                        'service': service.service.service,
                        'time': service.service.time,
                        'fee': service.service.fee,
                        'provider': str(request.user.provider.id),
                    },
                    'fees': str(service.fees),
                }
                invoice_services_data.append(service_data)

            invoice_data['invoice_services'] = invoice_services_data
            invoice_data['client'] = {
                "id": invoice.client.id,
                "first_name": invoice.client.first_name,
                "last_name": invoice.client.last_name,
            }
            return Response(invoice_data)
        if request.method == 'PATCH':
            invoice = get_object_or_404(ClientInvoice, pk=pk)
            request_data = request.data.copy()
            invoice.billing_type = int(request_data.get('billing_type', invoice.billing_type))
            deleted_ids_list = [item['service'] for item in request_data.get('services')]
            InvoiceServices.objects.filter().exclude(service__in=deleted_ids_list).delete()
            if 'services' in request_data:
                updates_fees = Decimal(0)
                for service in request_data.get('services'):
                    updates_fees += Decimal(service['fees'])
                    get_invoice_service = InvoiceServices.objects.filter(invoice=invoice, service_id=service['service']).first()
                    if get_invoice_service:
                        get_service = get_invoice_service.service
                        get_invoice_service.service = get_service
                        get_invoice_service.fees = service['fees']
                        get_invoice_service.save()
                    else:
                        get_service = ProviderServices.objects.get(id=service['service'])
                        client_invoice_service_payload = {
                            'invoice': invoice,
                            'date': datetime.fromtimestamp(int(service['date'])),
                            'service': get_service,
                            'client': invoice.client,
                            'fees': service['fees']
                        }
                        InvoiceServices.objects.create(**client_invoice_service_payload)
                invoice.fees = updates_fees
            invoice.save()
            # Add any further processing here
            return Response({'message': 'Invoice updated successfully.'}, status=status.HTTP_200_OK)
        if request.method == 'DELETE':
            invoice = ClientInvoice.objects.filter(id=pk).first()
            if invoice:
                invoice.delete()
                return Response({'message': 'Invoice has been deleted'}, status=status.HTTP_204_NO_CONTENT)
            else:
                return Response({'message': 'Invoice does not exist'}, status=status.HTTP_400_BAD_REQUEST)
    except Exception as e:
        return Response({
            'success': False,
            'message': f'Failed to get invoice data because of {str(e)}'
        }, status=status.HTTP_400_BAD_REQUEST)


@api_view(['POST'])
@permission_classes([IsProvider])
def create_client_invoice(request):
    try:
        request_data = request.data.copy()
        request_data['provider'] = request.user.id
        request_data['issue_date'] = timezone.now()
        request_data['due_date'] = timezone.now() + timedelta(days=7)

        invoice = ClientInvoice.objects.create(**request_data)
        return Response({'id': invoice.id}, status=status.HTTP_201_CREATED)
    except Exception as e:
        return Response({
            'success': False,
            'message': f'Failed to perform POST operation on create_client_invoice because of {str(e)}'
        }, status=status.HTTP_400_BAD_REQUEST)


@api_view(['PUT', 'PATCH'])
@permission_classes([IsProvider])
def update_client_invoice(request, pk):
    try:
        invoice = get_object_or_404(ClientInvoice, pk=pk)

        request_data = request.data.copy()
        if 'status' in request_data:
            if request_data['status'] == ClientInvoiceStatus.PAID.value:
                ClientPayment.objects.create(invoice=invoice, price=invoice.fees, date_received=datetime.now())

        for key, value in request_data.items():
            setattr(invoice, key, value)
        invoice.save()

        # Add any further processing here

        return Response({'id': invoice.id}, status=status.HTTP_200_OK)
    except Exception as e:
        return Response({
            'success': False,
            'message': f'Failed to perform {request.method} action on update_client_invoice because of {str(e)}'
        }, status=status.HTTP_400_BAD_REQUEST)

def create_client_payment(invoice_id, price_remain):
    ClientPayment.objects.create(
        invoice_id=invoice_id,
        price=price_remain,
        status=ClientPaymentType.STRIPE.value
    )


def update_paid_client_invoice(client_invoice_id):
    ClientInvoice.objects \
        .filter(id=client_invoice_id) \
        .update(status=ClientInvoiceStatus.PAID.value)


# @api_view(['POST'])
# @permission_classes([IsProvider])
# def charge_by_stripe(request, user, price, data, pk):
#     # Your code for charging by Stripe
#     try:
#         amount = int(price * SMALLEST_CURRENCY_UNIT)
#         stripe_account_id = ''
#
#         # serializer = ChargeStripeSerializer(data=data)
#         # serializer.is_valid(raise_exception=True)
#
#         if user.is_client:
#             stripe_account_id = user.client.user_provider.stripe_account_id
#             customer_id = user.client.stripe_account.stripe_id
#         elif user.is_provider:
#             stripe_account_id = user.provider.stripe_account_id
#
#         charge = stripe.Charge.create(
#             amount=amount,
#             currency='usd',
#             customer=customer_id,
#             transfer_data={'destination': stripe_account_id},  # Use transfer_data instead of destination
#             source=serializer.data['card_id'],
#             description=serializer.data['description']
#         )
#         create_client_payment(pk, price)
#         update_paid_client_invoice(pk)
#
#         return Response(charge, status=status.HTTP_201_CREATED)
#     except stripe.error.StripeError as e:
#         raise ValidationError({'error': e})


@api_view(['POST', 'GET'])
@permission_classes([IsProvider])
def super_bill_list(request):
    try:
        if request.method == 'GET':
            super_bills = SuperBill.objects.all()
            data = [{'id': super_bill.id, 'bill_number': super_bill.bill_number} for super_bill in super_bills]
            return Response(data, safe=False)
        elif request.method == 'POST':
            data = request.data.copy()
            try:
                with transaction.atomic():
                    super_bill = SuperBill.objects.create(bill_number=increment_super_bill_number())
                    client_invoice_ids = data['client_invoices']
                    client_invoices = ClientInvoice.objects.filter(id__in=client_invoice_ids)
                    super_bill.client_invoices.set(client_invoices)
                    # queryset = ClientInvoice.objects.all()

                    provider_id = str(request.user.provider.id)


                    data = []
                    for invoice in client_invoices:
                        invoice_data = {
                            'id': invoice.id,
                            'invoice_number': invoice.invoice_number,
                            'appointment_id': invoice.appointment_id,
                            'status': invoice.status,
                            'provider_id': provider_id,
                            'issue_date': str(int(invoice.issue_date.timestamp())) if invoice.issue_date else None,
                            'created_at': str(int(invoice.created_at.timestamp())) if invoice.created_at else None,
                            'first_name': invoice.client.first_name,
                            'last_name': invoice.client.last_name,
                            'billing_type': invoice.billing_type,
                            'fees': str(invoice.fees),
                            'due_date': str(int(invoice.due_date.timestamp())) if invoice.due_date else None,
                        }
                        invoice_services_data = []
                        for service in InvoiceServices.objects.filter(invoice=invoice):
                            service_data = {
                                'date': str(int(service.date.timestamp())) if service.date else None,
                                'service': {
                                    'id': str(service.service.id),
                                    'updated_at': str(int(service.service.updated_at.timestamp())),
                                    'created_at': str(int(service.service.created_at.timestamp())),
                                    'code': service.service.code,
                                    'service': service.service.service,
                                    'time': service.service.time,
                                    'fee': service.service.fee,
                                    'provider': str(request.user.provider.id),
                                },
                                'fees': str(service.fees),
                            }
                            invoice_services_data.append(service_data)

                        invoice_data['invoice_services'] = invoice_services_data
                        get_client_addresses = ClientAddress.objects.filter(client=invoice.client).all().values(
                            'address', 'state', 'city', 'zipcode')
                        invoice_data['client'] = {
                            "id": invoice.client.id,
                            "first_name": invoice.client.first_name,
                            "last_name": invoice.client.last_name,
                            "client_addresses": get_client_addresses
                        }
                        data.append(invoice_data)
                    response_dict = {
                        "id": super_bill.id, 'bill_number': super_bill.bill_number,
                        'client_invoices': data, 'created_at': str(int(super_bill.created_at.timestamp()))
                    }
                    return Response(response_dict, status=status.HTTP_201_CREATED)
            except Exception as e:
                return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)
    except Exception as e:
        return Response({
            'success': False,
            'message': f'Failed to perform {request.method} action on super_bill_list because of {str(e)}'
        }, status=status.HTTP_400_BAD_REQUEST)

@api_view(['GET', 'DELETE'])
@permission_classes([IsProvider])
def super_bill_detail(request, pk):
    try:
        try:
            super_bill = SuperBill.objects.get(pk=pk)
        except ObjectDoesNotExist:
            return Response({'error': 'SuperBill not found'}, status=status.HTTP_404_NOT_FOUND)

        if request.method == 'GET':
            client_invoices = super_bill.client_invoices.all()
            data = []

            for invoice in client_invoices:
                invoice_data = {
                    'id': invoice.id,
                    'invoice_number': invoice.invoice_number,
                    'appointment_id': invoice.appointment_id,
                    'status': invoice.status,
                    'provider_id': request.user.provider.id,
                    'issue_date': str(int(invoice.issue_date.timestamp())) if invoice.issue_date else None,
                    'created_at': str(int(invoice.created_at.timestamp())) if invoice.created_at else None,
                    'first_name': invoice.client.first_name,
                    'last_name': invoice.client.last_name,
                    'billing_type': invoice.billing_type,
                    'fees': str(invoice.fees),
                    'due_date': str(int(invoice.due_date.timestamp())) if invoice.due_date else None,
                }
                invoice_services_data = []
                for service in InvoiceServices.objects.filter(invoice=invoice):
                    service_data = {
                        'date': str(int(service.date.timestamp())) if service.date else None,
                        'service': {
                            'id': str(service.service.id),
                            'updated_at': str(int(service.service.updated_at.timestamp())),
                            'created_at': str(int(service.service.created_at.timestamp())),
                            'code': service.service.code,
                            'service': service.service.service,
                            'time': service.service.time,
                            'fee': service.service.fee,
                            'provider': str(request.user.provider.id),
                        },
                        'fees': str(service.fees),
                    }
                    invoice_services_data.append(service_data)

                invoice_data['invoice_services'] = invoice_services_data
                get_client_addresses = ClientAddress.objects.filter(client=invoice.client).all().values(
                    'address', 'state', 'city', 'zipcode')
                invoice_data['client'] = {
                    "id": invoice.client.id,
                    "first_name": invoice.client.first_name,
                    "last_name": invoice.client.last_name,
                    "client_addresses": get_client_addresses
                }
                data.append(invoice_data)
            response_dict = {
                "id": super_bill.id, 'bill_number': super_bill.bill_number,
                'client_invoices': data, 'created_at': str(int(super_bill.created_at.timestamp()))
            }
            return Response(response_dict)
        elif request.method == 'DELETE':
            super_bill.delete()
            return Response({'message': 'SuperBill deleted successfully'}, status=status.HTTP_204_NO_CONTENT)
    except Exception as e:
        return Response({
            'success': False,
            'message': f'Failed to perform {request.method} action on super_bill_detail because of {str(e)}'
        }, status=status.HTTP_400_BAD_REQUEST)


@api_view(['POST'])
@permission_classes([IsProvider])
def invoice_email_to_client(request, pk):
    try:
        superbill = SuperBill.objects.get(pk=pk)
        client_invoices = superbill.client_invoices.all()
        client = client_invoices[0].client
        from_email = settings.CONTACT_EMAIL
        to_email = client.email
        if 'file' in request.data:
            file_b64 = request.data['file']
            file_b64 = file_b64.split(',')[-1]
            # buffer = BytesIO.BytesIO()
            buffer = io.BytesIO()
            content = base64.b64decode(file_b64)
            buffer.write(content)

            html_template = get_template('client_superbill_email_template.html')
            context = {
                'full_name_provider': client.first_name + ' ' + client.last_name,
            }
            html_content = html_template.render(context)

            msg = EmailMultiAlternatives(
                subject="Ethera - Invoice", from_email=from_email, to=[to_email])
            msg.attach_alternative(html_content, "text/html")
            msg.attach(f"invoice_{superbill.bill_number}.pdf", buffer.getvalue(),
                       guess_type(f"invoice_{superbill.bill_number}.pdf")[0])

            try:
                msg.send()
                return Response({"response": "Email Sent"}, status=status.HTTP_200_OK)
            except Exception as ex:
                print(ex)
                return Response({"response": 'Failed to send email'}, status=status.HTTP_400_BAD_REQUEST)
        else:
            return Response({"error": "Please attach file."}, status=status.HTTP_400_BAD_REQUEST)
    except Exception as e:
        return Response({
            'success': False,
            'message': f'Failed to mail super bill to client because of {str(e)}'
        }, status=status.HTTP_400_BAD_REQUEST)


@api_view(['GET'])
@permission_classes([IsProvider])
def list_attendance(request):
    try:
        queryset = Attendance.objects.select_related(
            'appointment__provider_location',
            'client'
        ).all()

        status = request.GET.get('status', None)
        if status:
            queryset = queryset.filter(status=status)
        params = request.query_params
        if 'start_date' in params and 'end_date' not in params:
            start_date = params['start_date']
            queryset = queryset.filter(created_at__date=start_date)

        elif 'start_date' in params and 'end_date' in params:
            start_date = params['start_date']
            end_date = params['end_date']

            if start_date > end_date:
                raise ValidationError(
                    'Start date must be lower than end date.')

            queryset = queryset.filter(created_at__range=[start_date, end_date])

        data = []

        for attendance in queryset:
            appointment = attendance.appointment
            provider_location = appointment.provider_location
            booking_location = None

            if appointment.booking:
                booking_room_id = appointment.booking.room
                if booking_room_id:
                    booking_room = get_object_or_404(Room, id=booking_room_id)
                    booking_location = booking_room.location

            data.append({
                'appointment': {
                    'id': appointment.id,
                    'start_date': appointment.start_date.timestamp() if appointment.start_date else None,
                    'provider_location': {
                        'id': provider_location.id if provider_location else None,
                        'name': provider_location.name if provider_location else None
                    },
                    'booking': {
                        'id': booking_location.id if booking_location else None,
                        'location': {
                            'id': booking_location.id if booking_location else None,
                            'name': booking_location.name if booking_location else None
                        } if booking_location else None
                    } if booking_location else None
                },
                'client': {
                    'id': attendance.client.id,
                    'first_name': attendance.client.first_name,
                    'last_name': attendance.client.last_name,
                },
                'status': attendance.status
            })

        return paginated_queryset_response(data, request)
    except Exception as e:
        return Response({
            'success': False,
            'message': f'Failed to get attendance because of {str(e)}'
        }, status=status.HTTP_400_BAD_REQUEST)


@api_view(['GET'])
@permission_classes([IsProvider])
def retrieve_attendance(request, pk):
    try:
        attendance = Attendance.objects.get(pk=pk)
    except Attendance.DoesNotExist:
        return Response({'error': 'Attendance not found'}, status=404)

    appointment = attendance.appointment
    client = attendance.client
    provider_location = appointment.provider_location
    booking_location = provider_location.booking.location

    data = {
        'appointment': {
            'id': appointment.id,
            'start_date': appointment.start_date,
            'provider_location': {
                'id': provider_location.id,
                'name': provider_location.name
            },
            'booking': {
                'id': booking_location.id,
                'location': {
                    'id': booking_location.location.id,
                    'name': booking_location.location.name
                }
            }
        },
        'client': {
            'id': client.id,
            'first_name': client.first_name,
            'last_name': client.last_name,
            # Add other client fields as needed
        },
        'status': attendance.status
    }

    return Response(data)

@api_view(('POST',))
@permission_classes([IsProvider])
def invoice_email_to_client(request, pk=None):
    try:
        invoice = ClientInvoice.objects.filter(id=pk).first()
        user = request.user
        provider = None
        if user.is_provider:
            provider = user.provider
        invoice_setting = InvoiceSetting.objects.filter().first()
        from_email = invoice_setting.email

        client = invoice.client
        if 'file' in request.data:
            file_b64 = request.data['file']
            file_b64 = file_b64.split(',')[-1]
            buffer = io.BytesIO()
            content = base64.b64decode(file_b64)
            buffer.write(content)
            formatted_issue_date = invoice.issue_date.strftime("%B %d, %Y")

            context = {
                'message': invoice_setting.message,
                'number_of_invoices': invoice.invoice_number,
                'client_name': client.first_name,
                'provider_name': (provider.first_name + ' ' +
                                  provider.last_name),
                'provider_email': provider.email,
                'provider_phone_number': provider.phone_number,
                'issue_date': formatted_issue_date
            }
            message = invoice_setting.message
            if invoice_setting.include_logo and invoice_setting.logo_image:
                logo_template = f'<img src="{invoice_setting.logo_image}images/ethera_logo.png" alt="Logo"><br/>'
                message = logo_template + message
            if invoice_setting.footer_information:
                message = message + invoice_setting.footer_information
            message_template = Template(message)
            html_content = message_template.render(Context(context))
            msg = EmailMultiAlternatives(
                subject='Invoice for {date}'.format(date=invoice.created_at), from_email=from_email,
                to=[client.email])

            msg.attach_alternative(html_content, "text/html")

            msg.attach(f"invoice_{invoice.invoice_number}.pdf", buffer.getvalue(),
                       guess_type(f"invoice_{invoice.invoice_number}.pdf")[0])
            try:
                msg.send()
                return Response({"response": "Email Sent"}, status=status.HTTP_200_OK)
            except Exception as ex:
                print(ex)
                return Response({"response": "Failed to send email"}, status=status.HTTP_200_OK)
        else:
            return Response({"error": "Please attach file."}, status=status.HTTP_400_BAD_REQUEST)
    except Exception as e:
        print(e)
        return Response({'msg': 'Something went wrong'}, status=status.HTTP_400_BAD_REQUEST)